/* **********************************************************************************
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* **********************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;

namespace Theme_Builder
{
    // TODO: move this functionality to the utility project

    public class ColorMods
    {
        public double alpha = 100.0;
        public double tint = 100.0;
        public double shade = 100.0;
        public double hueMod = 100.0;
        public double satMod = 100.0;
        public double lumMod = 100.0;
        public double redMod = 100.0;
        public double blueMod = 100.0;
        public double greenMod = 100.0;
    }

    class HSLHelper
    {
        // converted from http://support.microsoft.com/kb/29240
        const int HLSMAX = 255;   /* H,L, and S vary over 0-HLSMAX */
        /* HLSMAX BEST IF DIVISIBLE BY 6 */
        const int RGBMAX = 255;   /* R,G, and B vary over 0-RGBMAX */
        /* RGBMAX, HLSMAX must each fit in a byte. */

        /* Hue is undefined if Saturation is 0 (grey-scale) */
        /* This value determines where the Hue scrollbar is */
        /* initially set for achromatic colors */
        const int UNDEFINED = (HLSMAX * 2 / 3);

        private int m_hue, m_lum, m_sat;

        private int Max(int a, int b)
        {
            if (a > b)
                return a;
            return b;
        }

        private int Min(int a, int b)
        {
            if (a < b)
                return a;
            return b;
        }

        public void RotateHue(double degrees)
        {
            int hue = m_hue + (int)(degrees * HLSMAX);
            m_hue = hue % HLSMAX;
        }

        public void ModLum(double mod)
        {
            int lum = (int)(m_lum * mod);
            m_lum = lum <= HLSMAX ? lum : HLSMAX;
        }

        public void ModSat(double mod)
        {
            int sat = (int)(m_sat * mod);
            m_sat = sat <= HLSMAX ? sat : HLSMAX;
        }

        public HSLHelper(int R, int G, int B)
        {
            m_hue = 0;
            m_lum = 0;
            m_sat = 0;

            int cMax, cMin;      /* max and min RGB values */
            int Rdelta, Gdelta, Bdelta; /* intermediate value: % of spread from max

          /* calculate lightness */
            cMax = Max(Max(R, G), B);
            cMin = Min(Min(R, G), B);
            m_lum = (((cMax + cMin) * HLSMAX) + RGBMAX) / (2 * RGBMAX);

            if (cMax == cMin)
            {           /* r=g=b --> achromatic case */
                m_sat = 0;                     /* saturation */
                m_hue = UNDEFINED;             /* hue */
            }
            else
            {                        /* chromatic case */
                /* saturation */
                if (m_lum <= (HLSMAX / 2))
                    m_sat = (((cMax - cMin) * HLSMAX) + ((cMax + cMin) / 2)) / (cMax + cMin);
                else
                    m_sat = (((cMax - cMin) * HLSMAX) + ((2 * RGBMAX - cMax - cMin) / 2))
                       / (2 * RGBMAX - cMax - cMin);

                /* hue */
                Rdelta = (((cMax - R) * (HLSMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin);
                Gdelta = (((cMax - G) * (HLSMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin);
                Bdelta = (((cMax - B) * (HLSMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin);

                if (R == cMax)
                    m_hue = Bdelta - Gdelta;
                else if (G == cMax)
                    m_hue = (HLSMAX / 3) + Rdelta - Bdelta;
                else /* B == cMax */
                    m_hue = ((2 * HLSMAX) / 3) + Gdelta - Rdelta;

                if (m_hue < 0)
                    m_hue += HLSMAX;
                if (m_hue > HLSMAX)
                    m_hue -= HLSMAX;
            }
        }

        /* utility routine for HLStoRGB */
        int HueToRGB(int n1, int n2, int hue)
        {
            /* range check: note values passed add/subtract thirds of range */
            if (hue < 0)
                hue += HLSMAX;

            if (hue > HLSMAX)
                hue -= HLSMAX;

            /* return r,g, or b value from this tridrant */
            if (hue < (HLSMAX / 6))
                return (n1 + (((n2 - n1) * hue + (HLSMAX / 12)) / (HLSMAX / 6)));
            if (hue < (HLSMAX / 2))
                return (n2);
            if (hue < ((HLSMAX * 2) / 3))
                return (n1 + (((n2 - n1) * (((HLSMAX * 2) / 3) - hue) + (HLSMAX / 12)) / (HLSMAX / 6))
          );
            else
                return (n1);
        }

        public void GetRGB(ref int R, ref int G, ref int B)
        {
            int Magic1, Magic2;       /* calculated magic numbers (really!) */

            if (m_sat == 0)
            {            /* achromatic case */
                R = G = B = (m_lum * RGBMAX) / HLSMAX;
                if (m_hue != UNDEFINED)
                {
                    System.Diagnostics.Debug.Assert(false, "Error converting color");
                }
            }
            else
            {                    /* chromatic case */
                /* set up magic numbers */
                if (m_lum <= (HLSMAX / 2))
                    Magic2 = (m_lum * (HLSMAX + m_sat) + (HLSMAX / 2)) / HLSMAX;
                else
                    Magic2 = m_lum + m_sat - ((m_lum * m_sat) + (HLSMAX / 2)) / HLSMAX;
                Magic1 = 2 * m_lum - Magic2;

                /* get RGB, change units from HLSMAX to RGBMAX */
                R = (HueToRGB(Magic1, Magic2, m_hue + (HLSMAX / 3)) * RGBMAX +
                   (HLSMAX / 2)) / HLSMAX;
                G = (HueToRGB(Magic1, Magic2, m_hue) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
                B = (HueToRGB(Magic1, Magic2, m_hue - (HLSMAX / 3)) * RGBMAX +
                   (HLSMAX / 2)) / HLSMAX;
            }
        }
    }
}
